#include "view.h"

/* If you are running a POSIX OS and have multiple cores or processors,
 * this might give you a boost.  Remember g++ -pthread
 */

//#define ENABLE_PTHREAD_SUPPORT

#ifdef ENABLE_PTHREAD_SUPPORT
#include <pthread.h>
#include <signal.h>
#endif

#include <cmath>

View::View(Scene *scene) : m_scene(scene), m_image(0)
{
}

View::~View()
{
  if (m_image != 0)
  {
    delete[] m_image;
  }
}
double dblRand()
{
  double v = (double)std::rand() / RAND_MAX;
  v -= 0.5;
  return v;
}
void View::m_renderSlice(unsigned int minX, unsigned int maxX)
{
  for (unsigned int x = minX; x < maxX; x++)
  {
    double yAng = m_yRot + m_hFov * x / m_width;
    for (unsigned int y = 0; y < m_height; y++)
    {
      double xAng = m_xRot + m_vFov * y / m_height;
      Color total;
      for (int i = 0; i < SAMPLE_NUM; i++)
      {
        const double X_DELTA = m_vFov / m_height,
                     Y_DELTA = m_hFov / m_width,
                     X_OFFSET = dblRand() * X_DELTA,
                     Y_OFFSET = dblRand() * Y_DELTA;
        Ray r;
        r.pos = m_pos;

        r.dir.x = std::cos(yAng + Y_OFFSET) * std::cos(xAng + X_OFFSET);
        r.dir.y = std::sin(xAng + X_OFFSET);
        r.dir.z = std::sin(yAng + Y_OFFSET) * std::cos(xAng + X_OFFSET);
        r.parent = 0;
        r.recursionLevel = 0;

        Color tmpColor;
        m_scene->cast(r, tmpColor);
        tmpColor *= (1.0 / SAMPLE_NUM);
        total += tmpColor;
      }
      m_image[x + y * m_width] = total;
    }
  }
}
void* View::m_renderSliceThreadHook(void *threadData)
{
  SliceData *sliceData = (SliceData*)threadData;
  sliceData->view->m_renderSlice(sliceData->minX, sliceData->maxX);
#ifdef ENABLE_PTHREAD_SUPPORT
  pthread_exit(NULL);
#endif
}
void View::render()
{
  const unsigned int THREAD_NUM = 4;
#ifdef ENABLE_PTHREAD_SUPPORT
  pthread_t  threads[THREAD_NUM];
#endif
  SliceData sliceDatas[THREAD_NUM];

#ifdef ENABLE_PTHREAD_SUPPORT
  pthread_attr_t attachableAttr;
  pthread_attr_init(&attachableAttr);
  pthread_attr_setdetachstate(&attachableAttr, PTHREAD_CREATE_JOINABLE);
#endif

  for (unsigned int i = 0; i < THREAD_NUM; i++)
  {
    sliceDatas[i].view = this;
    sliceDatas[i].minX = i * m_width / THREAD_NUM;
    sliceDatas[i].maxX = (i + 1) * m_width / THREAD_NUM;

#ifdef ENABLE_PTHREAD_SUPPORT
    int rc = pthread_create(&(threads[i]), &attachableAttr, 
                            View::m_renderSliceThreadHook,
                            (void*)&(sliceDatas[i]));
    if (rc)
    {
      std::cerr << "ERROR Spawning thread " << i << std::endl;
      std::exit(-1);
    }
#else
    View::m_renderSliceThreadHook((void*)&(sliceDatas[i]));
#endif
  }
#ifdef ENABLE_PTHREAD_SUPPORT
  pthread_attr_destroy(&attachableAttr);
  void *status;
  for (int i = 0; i < THREAD_NUM; i++)
  {
    pthread_join(threads[i], &status);
  }
#endif
}

void View::loadAttributes(std::istream &inData)
{
  inData >> m_width >> m_height >> m_hFov;
  
  m_pos.loadAttributes(inData);
  inData >> m_xRot >> m_yRot;
  
  inData.ignore();

  m_image = new Color[m_width * m_height];

  m_vFov = m_hFov * m_height / m_width;

  m_xRot -= m_vFov / 2;
  m_yRot -= m_hFov / 2;
}

void View::savePpm(std::ostream &outData)
{
  outData << "P3" << std::endl;
  outData << m_width << " " << m_height << std::endl;
  outData << 255 << std::endl;
  for (int y = 0; y < m_height; y++)
  {
    for (int x = 0; x < m_width; x++)
    {
      Color c = m_image[x + y * m_width];
      outData << c.toPpm() << " ";
    }
    outData << std::endl;
  }
}

void View::saveHtml(std::ostream &outData)
{
  outData << "<html><body>" << std::endl;
  outData << "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">" << std::endl;
  for (int y = 0; y < m_height; y++)
  {
    outData << "<tr>" << std::endl;
    for (int x = 0; x < m_width; x++)
    {
      Color c = m_image[x + y * m_width];
      outData << "<td width=\"1\" height=\"1\" bgcolor=\"" << c.toHtml() << "\"></td>";
    }
    outData << "</tr>" << std::endl;
  }
  outData << "</table>" << std::endl;
  outData << "</body></html>" << std::endl;
}
